/*
 * Copyright 2000-2020 ALPHA LAB.Inc All Rights Reserved.
 */
package com.alphalab.framework.context;
 
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; 
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.ReflectionUtils; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.alphalab.framework.domain.dto.ResultVO;
import com.alphalab.framework.exception.MoRuntimeException;
import com.alphalab.framework.log.IMoLog;
import com.alphalab.framework.log.MoLogFactory;
import com.alphalab.framework.types.CommonResultCodeEnum;
import com.alphalab.framework.util.ExecTimeCountor;
import com.alphalab.framework.util.SpringUtil;
import com.alphalab.framework.util.StringUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.util.JSONUtils;

/**
 * 处理前端请求并调用服务端方法的远程服务中心.
 * @author allen 
 * @version 1.1.0 2020年3月25日
 */
@RestController 
@RequestMapping("/remoteService")
public class RemoteService {
	
	/**
	 * logger.
	 */
	private static final IMoLog logger = MoLogFactory.getLog(RemoteService.class);
	
	/**
	 * 处理前端请求.
	 * @param request request
	 * @return Object
	 */
	@RequestMapping({"/handleRequest"})
	@ResponseBody
	public Object handleRequest(final HttpServletRequest request) {
		ExecTimeCountor exCounter = new ExecTimeCountor();
		final String serviceName = request.getParameter("serviceName");
		final String methodName = request.getParameter("methodName");
		String parameters = request.getParameter("parameters");
		logger.info("服务[{0}]的方法[{1}]调用开始，参数列[{2}]", serviceName, methodName, 
				parameters);
		// 解码
		try {
			parameters = URLDecoder.decode(parameters, "UTF-8");
		} catch (UnsupportedEncodingException ex) {
			logger.error("服务[{0}]的方法[{1}]参数列解码失败，参数列[{2}]", serviceName, methodName, 
					parameters);
			return new ResultVO(CommonResultCodeEnum.REMOTE_SERVICE_UNSUPPORTED_ENCODE.getCode(),
					CommonResultCodeEnum.REMOTE_SERVICE_UNSUPPORTED_ENCODE.getMsg(), ex);
		}
		
		/**
	     * 1.查找方法，不必根据参数列查询，因为controller层的方法名一定唯一.
	     */
	    Class interfaceClass = null;
		try {
			interfaceClass = Class.forName(serviceName);
		} catch (ClassNotFoundException ex) {
			logger.error("不存在名为[{0}]的服务", serviceName);
			return new ResultVO(CommonResultCodeEnum.REMOTE_SERVICE_CONTEXT_NOTFOUND_ERROR.getCode(),
					CommonResultCodeEnum.REMOTE_SERVICE_CONTEXT_NOTFOUND_ERROR.getMsg(), ex);
		}
		// getMethods返回的是接口的所有方法，包括自身的和从基类继承的
	    final Method[] methods = interfaceClass.getMethods();
	    Method targetMethod = null;
	    Type[] types = null;
	    for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				targetMethod = method;
			}
		}
	    if (targetMethod == null) {
	    	logger.error("服务[{0}]不存在名为[{1}]的方法", serviceName, methodName);
			return new ResultVO(CommonResultCodeEnum.REMOTE_SERVICE_METHOD_NOTFOUND_ERROR.getCode(),
					CommonResultCodeEnum.REMOTE_SERVICE_METHOD_NOTFOUND_ERROR.getMsg());
	    }
		types = targetMethod.getGenericParameterTypes();
		int index = 0;
		for (Type type : types) { 
			logger.debug("第{0}个参数的参数类型为{1}", (index + 1), type); 
			index++;
		}
	    
		/**
		 * 2.对参数数组进行类型转换，拼接参数列表.
		 */  
		// 添加日期类型的转换，如json对象中的long类型转成目标javabean对象的date类型 
		JSONUtils.getMorpherRegistry().registerMorpher(new TimestampMorpher());
		final Object[] parameObjs = new Object[types.length]; 
		final JSONArray jsonArray = JSONArray.fromObject(parameters); 
		if (types.length != jsonArray.size()) {
			return new ResultVO(CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_NOTMATCH_ERROR.getCode(),
					CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_NOTMATCH_ERROR.getMsg());	 
		}
		try {
			for (int i = 0; i < jsonArray.size(); i++) {
				final Object item = jsonArray.get(i); 
				if (item instanceof JSONArray) {
					// 对json数组进行类型转换
					// json数组中只考虑list集合的情况，不考虑其他map类型的传入，且list中不考虑再包含list
					final JSONArray jsonArr = JSONArray.fromObject(item);
					parameObjs[i] = getParameArr(jsonArr);
				} else if (item instanceof JSONObject) {
					// 对json对象进行类型转换
					final JSONObject job = JSONObject.fromObject(item);
					parameObjs[i] = getParameObj(job);
				} else {
					// 其他类型为字符串、数值，这里都不进行转换
					parameObjs[i] = item;
				}
			}		
		} catch (MoRuntimeException ex) {
			logger.error("类型转换失败", ex);
			return new ResultVO(ex);
		}
		
		/**
		 * 3.调用方法获取返回值.
		 */
		final String beanName = getBeanName(serviceName);
		logger.debug("bean Name = {0}", beanName);
		Object objcect = null;
		try {
			objcect = ReflectionUtils.invokeMethod(targetMethod, 
					SpringUtil.getBean(beanName), parameObjs);
		} catch (Exception ex) {
			logger.error("invokeMethod调用方法失败", ex);
			return new ResultVO(CommonResultCodeEnum.REMOTE_SERVICE_INVOKE_METHOD_ERROR.getCode(),
					CommonResultCodeEnum.REMOTE_SERVICE_INVOKE_METHOD_ERROR.getMsg(), ex);	 
		}
		Object result = null;
		if (objcect instanceof JSONObject) {
			result = JSONObject.fromObject(objcect);
		} else if (objcect instanceof JSONArray) {
			result = JSONArray.fromObject(objcect); 
		} else {
			result = objcect;
		}
		logger.info("方法返回结果为{0}", result);
		logger.info("服务[{0}]的方法[{1}]调用完成，耗时[{2}ms]", serviceName, methodName, 
				exCounter.getTotalExecTimeMillis());
		return result;
	} 		

	/**
	 * 将JSONObject强制类型转换为对应的java对象.
	 * @param job job
	 * @return Object
	 */
	private static Object getParameObj(final JSONObject job) throws MoRuntimeException {
		//如果是json对象，按约定需有jcls属性，否则这里可以抛个异常作为提示
		final String jobClsName = job.get("jcls") + "";
		if (StringUtil.isEmptyString(jobClsName)) { 
			logger.error("缺少jcls属性，请检查参数数组");
			throw new MoRuntimeException(
				CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_NOT_COMPLETE_ERROR.getCode(),
					CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_NOT_COMPLETE_ERROR.getMsg());
		}
		Class itemClass = null;
		Object itemObj = null;
		try {
			itemClass = Class.forName(jobClsName); 
		} catch (ClassNotFoundException e) {
			logger.error("没有找到名为{0}的类", jobClsName);
			throw new MoRuntimeException(
				CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_TYPE_NOTFOUND_ERROR.getCode(),
					CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_TYPE_NOTFOUND_ERROR.getMsg());
		} 
		itemObj = JSONObject.toBean(job, itemClass);
		// 判断json对象是否持有动态属性
		final List<String> dynamicAttrs = getDynamicAttrs(job, itemClass);
		String voName = "com.alphalab.framework.domain.DynamicValueObject";
		if (dynamicAttrs.size() > 0) { 
			// 然后判断该类是否动态对象，有则进行设置属性
			if (voName.equals(itemClass.getSuperclass().getName())) {
				try {
					// 设置动态属性到对象中
					setDynamicAttr(dynamicAttrs, job, itemObj, itemClass);
				} catch (MoRuntimeException ex) {
					throw ex;
				}
			}	 
		} 
		return itemObj;
	}	

	/**
	 * 将JSONArray强制类型转换为对应的list集合.
	 * @param jsonArr jsonArr
	 * @return Object
	 */
	private static Object getParameArr(final JSONArray jsonArr) throws MoRuntimeException {
		Object itemObj = null;
		if (jsonArr.size() > 0) {
			//获得第一个集合元素，看是否为对象
			final Object firstItemObj = jsonArr.get(0);
			if (firstItemObj instanceof JSONObject) {
				final JSONObject arrJob = JSONObject.fromObject(firstItemObj);
				final String jobClsName = arrJob.get("jcls") + "";
				String invalidString = "null";
				//如果为对象，则进行类型转换
				if (StringUtil.isEmptyString(jobClsName) || invalidString.equals(jobClsName)) { 
					logger.error("缺少jcls属性，请检查参数数组");
					throw new MoRuntimeException(
						CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_NOT_COMPLETE_ERROR.getCode(),
							CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_NOT_COMPLETE_ERROR.getMsg());
				}
				Class itemClass = null;
				try {
					itemClass = Class.forName(jobClsName);
				} catch (ClassNotFoundException e) {
					logger.error("没有找到名为{0}的类", jobClsName);
					throw new MoRuntimeException(
						CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_TYPE_NOTFOUND_ERROR.getCode(),
							CommonResultCodeEnum.REMOTE_SERVICE_PARAMETER_TYPE_NOTFOUND_ERROR.getMsg());
				}
				itemObj = JSONArray.toCollection(jsonArr, itemClass); 
			} else if (firstItemObj instanceof String) {
				//如果为字符串
				itemObj = JSONArray.toArray(jsonArr, String.class); 
			} else if (firstItemObj instanceof Integer) {
				//如果为整数类型
				itemObj = JSONArray.toArray(jsonArr, Integer.class); 
			} else if (firstItemObj instanceof Double) {
				//如果为小数类型
				itemObj = JSONArray.toArray(jsonArr, Double.class);  
			}  
		} else {
			itemObj = null;
		} 		
		return itemObj;
	} 
	
	/**
	 * 获取不在class中的JSONObject的属性名集合.
	 * @param job JSONObject
	 * @param itemClass Class
	 * @return String[]
	 */
	private static List<String> getDynamicAttrs(final JSONObject job, final Class itemClass) {
		final List<String> dynamicAttrs = new ArrayList<String>();
		// 获取本类全部属性
		String allClassFields = "";
		final Field[] fields = itemClass.getDeclaredFields();
		for (Field field : fields) {
			allClassFields += field.getName() + ",";
		}		 
		// 获取前端全部属性 
		for (Iterator<?> iterator = job.keys(); iterator.hasNext();) {
			final String type = (String) iterator.next(); 
			// 获取不在类的属性
			if (allClassFields.indexOf(type) == -1) {
				dynamicAttrs.add(type);
			}
		} 
		return dynamicAttrs;
	} 
	
	/**
	 * 对目标对象设置动态属性.
	 * @param dynamicAttrs 动态属性集合
	 * @param job 解析后的前端json对象
	 * @param targetObj 设置动态属性的目标object
	 * @param itemClass 目标类的class
	 */
	private static void setDynamicAttr(final List<String> dynamicAttrs, final JSONObject job, 
			final Object targetObj, final Class itemClass) throws MoRuntimeException {
		try {
			for (String filedName : dynamicAttrs) { 
				// 动态属性名
				final String dynamicAttr = filedName;
				// 动态属性值
				final Object dynamicValue = job.get(filedName);  
				// 对object调用set方法
				final Object[] args = new Object[2];
				args[0] = dynamicAttr;
				args[1] = dynamicValue;
				// 找到set方法
			    final Method[] methods = itemClass.getMethods();
			    Method targetMethod = null;
			    for (Method method : methods) {
					if ("set".equals(method.getName())) {
						targetMethod = method;
					}
				}
		    	// 调用set方法
				targetMethod.invoke(targetObj, args);
			}
		} catch (Exception ex) {
			throw new MoRuntimeException(
					CommonResultCodeEnum.REMOTE_SERVICE_SET_DYNAMICATTR_ERROR.getCode(), ex);
		}
	} 
	
	/**
	 * 根据服务名返回bean名.
	 * @param serviceName serviceName
	 * @return String
	 */
	private String getBeanName(final String serviceName) {
		String beanName = "";
		final String[] items = serviceName.split("\\.");
		final String interfaceName = items[items.length - 1]; 
		beanName = interfaceName.substring(1, interfaceName.length());
		return toLowerCaseFirstOne(beanName);
	}
	
	/**
	 * 首字母小写.
	 * @param s s
	 * @return String
	 */
	public static String toLowerCaseFirstOne(final String s) {
	    if (Character.isLowerCase(s.charAt(0))) {
	    	return s;
	    } else {
			return (new StringBuilder())
					.append(Character.toLowerCase(s.charAt(0)))
					.append(s.substring(1)).toString();
	    } 
	}
	
}
